/*
 * MIT License
 *
 * Copyright (c) 2024-2048 冰羽
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package cn.star.framework.monitor.aspect;

import cn.star.framework.core.Constant;
import cn.star.framework.monitor.log.Logger;
import cn.star.framework.monitor.log.LoggerEvent;
import cn.star.framework.monitor.MonitorContext;
import cn.star.framework.monitor.MonitorInvokeProxy;
import cn.star.framework.monitor.MonitorInvokeProxy.InvokeType;
import cn.star.framework.monitor.MonitorType;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

/**
 * 拦截处理<br>
 *
 * @author zhaoweiping
 *     <p style='color: red'>Created on 2024-09-23 11:06:00
 * @since 3.0.0
 */
@Slf4j
@Aspect
@Component
public class MonitorMethodAspect {

  @Value("${star.env:}")
  private String env;

  @Autowired private MonitorInvokeProxy proxy;
  @Autowired private ApplicationEventPublisher applicationEventPublisher;

  @Around(
      value =
          "@annotation(" + Constant.BASE_PACKAGE + ".framework.monitor.annotation.MonitorMethod)")
  public Object monitor(ProceedingJoinPoint p) throws Throwable {
    MonitorContext context = new MonitorContext(p);

    this.checkDemoEnvironment(context);

    proxy.invoke(context, InvokeType.BEFORE);
    proxy.invokeAsync(context, InvokeType.BEFORE_ASYNC);

    if (context.getMonitorTypes().contains(MonitorType.METHOD_EXECUTE_TIME)) {
      long start = System.currentTimeMillis();
      Object methodExecuteResult = p.proceed();
      long end = System.currentTimeMillis();
      context.setMethodResult(methodExecuteResult);
      context.setDurationInMillis(end - start);
      if (log.isDebugEnabled()) {
        log.debug(
            "method {} execute duration: {} ms",
            context.getMethodName(),
            context.getDurationInMillis());
      }
    } else {
      context.setMethodResult(p.proceed());
    }

    proxy.invoke(context, InvokeType.AFTER);
    proxy.invokeAsync(context, InvokeType.AFTER_ASYNC);

    if (context.getMonitorTypes().contains(MonitorType.WRITE_LOG)) {
      Logger logger = Logger.to(context);
      applicationEventPublisher.publishEvent(new LoggerEvent(logger));
    }

    return context.getMethodResult();
  }

  private void checkDemoEnvironment(MonitorContext context) {
    List<MonitorType> types = context.getMonitorTypes();
    if (types.contains(MonitorType.DEMO_ENVIRONMENT_DISABLED)
        && Constant.DEMO_ENVIRONMENT.equalsIgnoreCase(env)) {
      throw new RuntimeException("演示环境，不允许操作");
    }
  }
}
